package away3d.cameras.lenses
{
	import away3d.arcane;
	import away3d.containers.*;
	import away3d.core.base.*;
	import away3d.core.geom.*;
	
	import flash.geom.*;
	
	use namespace arcane;
	
	public class ShiftLens extends AbstractLens
	{
		private var _offset:Number = 1;
		public var r:Number = 1160;
		public var l:Number = 1;
		public var t:Number = 1160;
		public var b:Number = 1;
		public var n:Number = 1000;
		public var f:Number = Infinity;
		
		public var headX:Number = 1;  //represent inches()
		public var headY:Number = 1;  //represent inches()
		public var headDist:Number = 36;
		public var dpi:Number = 72;
		public var headAngle:Number = 0;
		
		public var _hd;
		public var _hoX;
		public var _hoY;
		
		/** @private */
		arcane override function setView(val:View3D):void
		{
			super.setView(val);
			
			if (_clipping.minZ == -Infinity)
        		_near = -_camera.focus/2;
        	else
        		_near = _clipping.minZ;

			//trace("focus is " + _camera.focus);
			//_projectionMatrix.rawData = Vector.<Number>([_camera.zoom*_camera.focus, 0, 0, 0, 0, _camera.zoom*_camera.focus, 0, 0, _offset*0.001, 0, 1, 1, 0, 0, _camera.focus, _camera.focus]);
			_projectionMatrix.rawData = Vector.<Number>([_camera.zoom*_camera.focus, 0, 0, 0, 0, _camera.zoom*_camera.focus, 0, 0, 0, 0, 1, 1, 0, 0, _camera.focus, _camera.focus]);
			
		}
		/** @private */
		arcane override function getFrustum(node:Object3D, viewTransform:Matrix3D):Frustum
		{

			_frustum = _cameraVarsStore.createFrustum(node);
			_focusOverZoom = _camera.focus/_camera.zoom;
			_zoom2 = _camera.zoom*_camera.zoom;
			
			_plane = _frustum.planes[Frustum.NEAR];
			_plane.a = 0;
			_plane.b = 0;
			_plane.c = 1;
			_plane.d = -_near;
			_plane.transform(viewTransform);
			
			_plane = _frustum.planes[Frustum.FAR];
			_plane.a = 0;
			_plane.b = 0;
			_plane.c = -1;
			_plane.d = _far;
			_plane.transform(viewTransform);
			
			_plane = _frustum.planes[Frustum.LEFT];
			_plane.a = _clipHeight*_focusOverZoom;
			_plane.b = 0;
			//_plane.c = -_clipHeight*_clipLeft/_zoom2;
			_plane.c = -1000;
			_plane.d = _plane.c*_camera.focus;
			_plane.transform(viewTransform);
			
			_plane = _frustum.planes[Frustum.RIGHT];
			_plane.a = -_clipHeight*_focusOverZoom;
			_plane.b = 0;
			_plane.c = (_clipHeight*_clipRight/_zoom2);
			_plane.d = _plane.c*_camera.focus;
			_plane.transform(viewTransform);
			
			_plane = _frustum.planes[Frustum.TOP];
			_plane.a = 0;
			_plane.b = _clipWidth*_focusOverZoom;
			_plane.c = -_clipWidth*_clipTop/_zoom2;
			_plane.d = _plane.c*_camera.focus;
			_plane.transform(viewTransform);
			
			_plane = _frustum.planes[Frustum.BOTTOM];
			_plane.a = 0;
			_plane.b = -_clipWidth*_focusOverZoom;
			_plane.c = _clipWidth*_clipBottom/_zoom2;
			_plane.d = _plane.c*_camera.focus;
			_plane.transform(viewTransform);
						
			return _frustum;
		}
		/** @private */
		arcane override function getFOV():Number
		{
			//calculated from the arctan addition formula arctan(x) + arctan(y) = arctan(x + y / 1 - x*y)
			return Math.atan2(_clipHeight, _camera.focus*_camera.zoom + _clipTop*_clipBottom/(_camera.focus*_camera.zoom))*toDEGREES;
		}
		/** @private */
		arcane override function getZoom():Number
		{
			var b:Number = _clipHeight/Math.tan(_camera.fov*toRADIANS);
			return (b + Math.sqrt(Math.pow(b, 2) - 4*_clipTop*_clipBottom/_camera.zoom))/(2*_camera.focus);
		}
		/** @private */
		arcane override function getT(screenZ:Number):Number
		{
			return 1/(_camera.focus + screenZ);
		}
		/** @private */
		arcane override function getScreenZ(t:Number):Number
		{
			return 1/t - _camera.focus;
		}
		/** @private */
		arcane override function project(viewTransform:Matrix3D, verts:Vector.<Number>, screenVerts:Vector.<Number>, uvts:Vector.<Number>):void
		{
			
			var obliqueTransform:Matrix3D = new Matrix3D();
			var turnTransform:Matrix3D = new Matrix3D();
			var skewTransform:Matrix3D = new Matrix3D();
			//obliqueTransform.rawData = Vector.<Number>([_camera.zoom*_camera.focus, 0, 0, 0,     0, _camera.zoom*_camera.focus, 0, 0,     _offset*0.001, 0, 1, 1,     0, 0, _camera.focus, _camera.focus]);
			//obliqueTransform.rawData = Vector.<Number>([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1]);
			//obliqueTransform.rawData = Vector.<Number>([1, 0, 0, 0,  0, 1, 0, 0,  0, 0, 1, 1, 0, 0, 1, 1]);
			//obliqueTransform.rawData = Vector.<Number>([1, 0, 0, 0,    0, 0, 0, 0,     0, 0, 1, 1,     0, 0, 1, 2]);
			//1, 0, 0, 0,      0, 1, 0, 0,   0, 0, 1, 1/Ez,   -Ex, -Ey, 0, 0]   where e is viewer position
			//obliqueTransform.rawData =Vector.<Number>([100, 0, 0, 0,       0, 100, 0, 0,      -1, -1, 1 , 1,     0, 0, -1, 0]);
			//obliqueTransform.rawData = Vector.<Number>([ (2*n)/(r-l)*50, 0, 0, 0,       0, (2*n)/(t-b)*50, 0, 0,      -1, -1, f/(f-n), 1,      0, 0, 0-(n*f)/(f-n), 0]); //unity
			
			//PerspectiveMatrixOffCenterLH
			/*
2*znearPlane/(right-left)   0                           0                                              0
0                           2*znearPlane/(top-bottom)   0                                              0
(left+right)/(right-left)  (top+bottom)/(top-bottom)    zfarPlane/(znearPlane-zfarPlane)              -1
0                           0                           znearPlane*zfarPlane/(znearPlane-zfarPlane)    0
 
 order of add is left right bottome top zNeraPlane zFarPlane
 * 
 */

			
			
			
			//var turnTransform:Matrix3D = new Matrix3D();
			//turnTransform.rawData = Vector.<Number>([1, 0, 0, 0,    0, 1, 0, 0,    0, 0, 1, 0,      0, 0, 0, 1]);
			//skewTransform.rawData = Vector.<Number>([1 + (headAngle*.1), 0, 0, 0,    0, 1, 0, 0,    0, 0, 1, 0,      0, 0, 0, 1]);
			//skewTransform.rawData = Vector.<Number>([1.7, 0, 1.01, 0,    0, 1.7, 1.01, 0,    0, 0, 1, 1,      0, 0, -1, 0]);
			
			var nearPlane = .05;
			//var nearPlane = -0.05;
			var screenAspect = .5;
			//var screenAspect = .005;
			
			var left:Number = nearPlane*(-.5*screenAspect + headX)/headDist;
			var right:Number = nearPlane*(.5*screenAspect + headX)/headDist;
			var bottom:Number = nearPlane*(-.5 - headY)/headDist;
			var top:Number = nearPlane*(.5 - headY)/headDist;
			var znearPlane:Number = nearPlane;
			var zfarPlane = 1000;
			
			var test:Matrix3D = makePerspectiveOffCenterTranform(left, right, bottom, top, znearPlane, zfarPlane);
			//_screenMatrix.rawData = _projectionMatrix.rawData;
			_screenMatrix.rawData = test.rawData;
			_screenMatrix.prepend(test);
			_screenMatrix.prepend(viewTransform);
			
			trace("verts pre " + screenVerts);
   	
        	Utils3D.projectVectors(_screenMatrix, verts, screenVerts, uvts);

			trace("verts post " + screenVerts);
        	
        }
		
        /**
         * @inheritDoc
         */
		public override function getPerspective(screenZ:Number):Number
		{
			return _camera.zoom / (1 + screenZ / _camera.focus);
		}
		
		public function updatePerspective(hd:Number, hoX:Number, hoY:Number):void {	
			
			_hd = hd;
			_hoX = hoX;
			_hoY = hoY;
			
			
		}
		
		public function scewProjection(m:Matrix3D, verts:Vector.<Number>, projectedVerts:Vector.<Number>, uvts:Vector.<Number>):void
		{
			
			trace("scewProjection");
			trace("m " + m);
			trace("verts "+verts);
			trace("projectedVerts " + projectedVerts);
			trace("uvts " + uvts);
		
		}
		
		public function makePerspectiveOffCenterTranform(left:Number, right:Number, bottom:Number, top:Number, znearPlane, zfarPlane):Matrix3D
		{
			
			var tran:Matrix3D = new Matrix3D();
			
			/*
			 * works when applied twice
			var cOne:Vector.<Number> =  Vector.<Number>([    znearPlane/(right-left),  0,  (left+right)/(right-left), 0 ]);
			var cTwo:Vector.<Number> = Vector.<Number>([     0,    2*znearPlane/(top-bottom),   (top+bottom)/(top-bottom),   0]);
			var cThree:Vector.<Number> =  Vector.<Number>([   0,   0,   zfarPlane/(znearPlane-zfarPlane),  -1]);
			var cFour:Vector.<Number> =  Vector.<Number>([   0,   0,   -1,   0]);
			*/
			var cOne:Vector.<Number> =  Vector.<Number>([   znearPlane/(right-left),  0,  (left+right)/(right-left), 0 ]);
			var cTwo:Vector.<Number> = Vector.<Number>([     0,    2*znearPlane/(top-bottom),   (top+bottom)/(top-bottom),   0]);
			var cThree:Vector.<Number> =  Vector.<Number>([   (left+right)/(right-left),  (top+bottom)/(top-bottom),  zfarPlane/(znearPlane-zfarPlane),  -1]);
			var cFour:Vector.<Number> =  Vector.<Number>([   0,   0,   znearPlane*zfarPlane/(znearPlane-zfarPlane),   0]);
			var combined:Vector.<Number> =  cOne.concat(cFour, cThree, cTwo );
			
			tran.rawData = combined;

			return tran;
		}
	}
}